/*
** Xin YUAN, 2023, BSD (2)
*/

////////////////////////////////////////////////////////////////////////////////

//Client-Server framework

//------------------------------------------------------------------------------
//handle_data_base

/*! \brief An enumerator for handle operation types.

An enumerator for handle operation types.
*/
enum {
	OP_TYPE_NONE = 0,  //!< Operation None
	OP_TYPE_ACCEPT,    //!< Accepting operation
	OP_TYPE_CONNECT,   //!< Connecting operation
	OP_TYPE_RECV,      //!< Reading operation
	OP_TYPE_SEND,      //!< Writing operation
	OP_TYPE_CLOSE,
	OP_TYPE_USER       //!< Base value for user defined
};

#define HANDLE_BUFFER_SIZE  (2048)

class handle_data_base
{
private:
	ns_socket sock;
	uintptr_t index;
	uint8_t recv_buffer[HANDLE_BUFFER_SIZE + sizeof(uint32_t)];
	uint32_t recv_len;
	//critical section
	uint8_t send_buffer[HANDLE_BUFFER_SIZE];
	volatile uint32_t send_len;
	uint32_t act_sent_len;
	int op_type;  //OP_TYPE_*

	bool is_valid_index() const noexcept
	{
		return index != (uintptr_t)-1;
	}

protected:
	handle_data_base() noexcept : index((uintptr_t)-1),
					recv_len(0), send_len(0), act_sent_len(0),
					op_type(OP_TYPE_NONE)
	{
	}
	handle_data_base(const handle_data_base&) = delete;
	~handle_data_base() noexcept
	{
		sock.GetSocket().GraceClose();
	}
	handle_data_base& operator=(const handle_data_base&) = delete;

public:
	const uint8_t* GetRecvBuffer() const noexcept
	{
		return recv_buffer;
	}
	uint8_t* GetRecvBuffer() noexcept
	{
		return recv_buffer;
	}
	uint32_t GetRecvLength() const noexcept
	{
		return recv_len;
	}
	void SetRecvLength(uint32_t len) noexcept
	{
		recv_len = len;
	}

	const uint8_t* GetSendBuffer() const noexcept
	{
		return send_buffer;
	}
	uint8_t* GetSendBuffer() noexcept
	{
		return send_buffer;
	}
	uint32_t GetSendLength() const noexcept
	{
		return send_len;
	}
	void SetSendLength(uint32_t len) noexcept
	{
		send_len = len;
	}
	uint32_t GetActSentLength() const noexcept
	{
		return act_sent_len;
	}
	void ClearActSentLength() noexcept
	{
		act_sent_len = 0;
	}

private:
	friend class client_base;
	friend class server_base;

	template <uint32_t, uint32_t>
	friend class client_util;
};

//------------------------------------------------------------------------------
//client

class client_base;

template <uint32_t t_uMaxHandleNum, uint32_t t_uMaxChangedNum>
class client_util
{
private:
	enum {
		CB_WAIT_ERROR = 0,
		CB_TIMEOUT,
		CB_CONNECTED,
		CB_RECV,
		CB_SENT,
		CB_CLOSED
	};
	typedef void (*client_callback)(int type, handle_data_base* p, bool succeeded, client_base* pv);

private:
	client_callback callback{NULL};

	//mutex
	sync_mutex m_mutex;

	bool init_mutex() noexcept
	{
		return m_mutex.Init();
	}
	void lock_mutex() noexcept
	{
		m_mutex.Lock();
	}
	void unlock_mutex() noexcept
	{
		m_mutex.Unlock();
	}

	//changed handles
	handle_data_base* m_changed_handles[t_uMaxChangedNum];
	CTL::CyclicQueueUtility<uint32_t> m_queue{t_uMaxChangedNum};
	uint32_t m_connect_num{0};

	//changed
	void reset_changed() noexcept
	{
		m_queue.Clear();
		m_connect_num = 0;
	}
	bool is_no_changed() const noexcept
	{
		return m_queue.IsEmpty();
	}
	bool set_changed(handle_data_base* p, int op) noexcept
	{
		//check close
		if ( p->op_type == OP_TYPE_CLOSE )
			return true;
		//check full
		uint32_t rear = m_queue.GetRear();
		uint32_t next = m_queue.CalcNext(rear);
		if ( m_queue.IsFront(next) ) //full
			return false;
		//check connect
		if ( op == OP_TYPE_CONNECT ) {
			if ( m_connect_num >= t_uMaxHandleNum )
				return false;
			++ m_connect_num;
		}
		//put
		p->op_type = op;
		uint32_t i = m_queue.GetFront();
		while ( i != rear ) {
			if ( m_changed_handles[i] == p )
				return true;
			i = m_queue.CalcNext(i);
		}
		m_changed_handles[rear] = p;
		m_queue.SetRear(next);
		return true;
	}
	handle_data_base* get_changed() const noexcept
	{
		return m_changed_handles[m_queue.GetFront()];
	}
	void skip_changed() noexcept
	{
		m_queue.De();
	}
	void dec_connect_changed() noexcept
	{
		-- m_connect_num;
	}

private:
	friend class client_base;
	template <class U>
	friend class client_impl;
};

#if defined(WIN32)

#include "windows/client_base.h"

#elif defined(__linux__)

#include "linux/client_base.h"

#else

#error This OS system is not supported!

#endif

//client_impl<T>

template <class T>
class client_impl : public client_base
{
private:
	typedef client_impl<T>  thisClass;

protected:
	client_impl() noexcept
	{
	}
	client_impl(const client_impl&) = delete;
	~client_impl() noexcept
	{
	}

	client_impl& operator=(const client_impl&) = delete;

public:
	bool Initialize() noexcept
	{
		return init(&client_cb);
	}

private:
	static void client_cb(int type, handle_data_base* p, bool succeeded, client_base* pv)
	{
		thisClass* pThis = static_cast<thisClass*>(pv);
		T* pT = static_cast<T*>(pThis);
		switch ( type ) {
		case utilClass::CB_WAIT_ERROR:
			pT->DoWaitError();  //may throw
			break;
		case utilClass::CB_TIMEOUT:
			pT->DoTimeout();  //may throw
			break;
		case utilClass::CB_CONNECTED:
			pT->DoConnected(p, succeeded);  //may throw
			break;
		case utilClass::CB_RECV:
			pT->DoRecv(p, succeeded);  //may throw
			break;
		case utilClass::CB_SENT:
			pT->DoSent(p, succeeded);  //may throw
			break;
		case utilClass::CB_CLOSED:
			pT->DoClosed(p);  //may throw
			break;
		default:
			break;
		}
	}

protected:
//overrideables
	//void DoWaitError();
	//void DoTimeout();
	//void DoConnected(handle_data_base* p, bool succeeded);
	//void DoRecv(handle_data_base* p, bool succeeded);
	//void DoSent(handle_data_base* p, bool succeeded);
	//void DoClosed(handle_data_base* p);
};

//------------------------------------------------------------------------------
//server

class handle_server_data_base;
class server_base;

class server_util
{
private:
	enum {
		CB_BEGIN_ACCEPT = 0,
		CB_ACCEPTED,
		CB_CONNECTED,
		CB_RECV,
		CB_SENT,
		CB_TIMEOUT,
		CB_CLOSED_BEGIN,
		CB_CLOSED_END
	};
	typedef uintptr_t (*server_callback)(int type, handle_server_data_base* p, bool succeeded, server_base* pv);  //noexcept

private:
	server_callback callback{NULL};

	uint32_t timeout{0};

	void init_timeout(uint32_t tm) noexcept
	{
		//timeout
		constexpr const uint32_t c_min_timeout = 2 * 60 * 1000;  //2 minutes
		if ( tm < c_min_timeout )
			tm = c_min_timeout;
		timeout = tm;
	}

private:
	friend class server_base;
	template <class U>
	friend class server_impl;
};

#if defined(WIN32)

#include "windows/server_base.h"

#elif defined(__linux__)

#include "linux/server_base.h"

#else

#error This OS system is not supported!

#endif

//server_impl<T>

template <class T>
class server_impl : public server_base
{
private:
	typedef server_impl<T>  thisClass;

protected:
	server_impl() noexcept
	{
	}
	server_impl(const server_impl&) = delete;
	~server_impl() noexcept
	{
	}

	server_impl& operator=(const server_impl&) = delete;

public:
	bool Initialize(uint32_t tm) noexcept
	{
		return init(&server_cb, tm);
	}

	static uintptr_t server_cb(int type, handle_server_data_base* p, bool succeeded, server_base* pv) noexcept
	{
		thisClass* pThis = static_cast<thisClass*>(pv);
		T* pT = static_cast<T*>(pThis);
		uintptr_t ret = 0;
		switch ( type ) {
		case utilClass::CB_BEGIN_ACCEPT:
			ret = (uintptr_t)(pT->DoBeginAccept());
			break;
		case utilClass::CB_ACCEPTED:
			pT->DoAccepted(p, succeeded);
			break;
		case utilClass::CB_CONNECTED:
			pT->DoConnected(p, succeeded);
			break;
		case utilClass::CB_RECV:
			pT->DoRecv(p, succeeded);
			break;
		case utilClass::CB_SENT:
			pT->DoSent(p, succeeded);
			break;
		case utilClass::CB_TIMEOUT:
			pT->DoTimeout(p);
			break;
		case utilClass::CB_CLOSED_BEGIN:
			pT->DoClosedBegin(p);
			break;
		case utilClass::CB_CLOSED_END:
			pT->DoClosedEnd(p);
			break;
		default:
			break;
		}
		return ret;
	}

protected:
//overrideables
	//handle_server_data_base* DoBeginAccept() noexcept;
	//void DoAccepted(handle_server_data_base* p, bool succeeded) noexcept;
	//void DoConnected(handle_server_data_base* p, bool succeeded) noexcept;
	//void DoRecv(handle_server_data_base* p, bool succeeded) noexcept;
	//void DoSent(handle_server_data_base* p, bool succeeded) noexcept;
	//void DoTimeout(handle_server_data_base* p) noexcept;
	//void DoClosedBegin(handle_server_data_base* p) noexcept;
	//void DoClosedEnd(handle_server_data_base* p) noexcept;
};

////////////////////////////////////////////////////////////////////////////////
